package org.redmagic.client.spring;

import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import org.redmagic.spring.RedMagicStringValueResolver;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionVisitor;
import org.springframework.util.Assert;

public class RedMagicBeanDefinitionVisitor extends BeanDefinitionVisitor {

	private RedMagicStringValueResolver valueResolver;

	private BeanDefinition parentBeanDefinition;
	
	private final Set<String> placeholders;
	
	private final Set<BeanDefinition> beanDefinitionsWithPlaceholders;
	
	private boolean discover = true;

	/**
	 * Create a new BeanDefinitionVisitor, applying the specified
	 * value resolver to all bean metadata values.
	 * @param valueResolver the StringValueResolver to apply
	 */
	public RedMagicBeanDefinitionVisitor(RedMagicStringValueResolver valueResolver) {

		super();
		
		Assert.notNull(valueResolver, "StringValueResolver must not be null");
		this.valueResolver = valueResolver;
		this.placeholders = new HashSet<String>();
		beanDefinitionsWithPlaceholders = new HashSet<BeanDefinition>();
	}

	public void collectBeanDefinition(BeanDefinition beanDefinition) {
		discover = true;
		parentBeanDefinition = beanDefinition;
		
		visitBeanDefinition(beanDefinition);
	}
	
	public void resolvePlaceholders(Properties resolvedProperties) {
		discover = false;
		this.valueResolver.setResolvedProperties(resolvedProperties);
		
		for (BeanDefinition beanDefinition : beanDefinitionsWithPlaceholders) {
			this.visitBeanDefinition(beanDefinition);
		}
	}
	
	/**
	 * Resolve the given String value, for example parsing placeholders.
	 * @param strVal the original String value
	 * @return the resolved String value
	 */
	@Override
	protected String resolveStringValue(String strVal) {
		
		if (discover) {
			Set<String> placeholders = this.valueResolver.resolveStringValueAsync(strVal);
			if (!placeholders.isEmpty()) {
				beanDefinitionsWithPlaceholders.add(parentBeanDefinition);
			}
			this.placeholders.addAll(placeholders);
			return strVal;
		} else {
			String resolvedValue = this.valueResolver.resolveStringValue(strVal);
			// Return original String if not modified.
			return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
		}
	}
	
	public Set<String> getPlaceholders() {
		return placeholders;
	}
}
